package com.yunxin.admin.system.infra.framework.syslog;

import com.yunxin.admin.system.domain.model.log.Log;
import com.yunxin.admin.system.domain.model.log.LogRepository;
import com.yunxin.core.utils.infra.utils.IpUtils;
import com.yunxin.admin.system.infra.framework.security.util.SecurityUtils;
import com.yunxin.common.utils.JacksonUtils;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Objects;

/**
 * @author zero°
 * @since 2023-07-05
 */
@Aspect
@Component
@Slf4j
@RequiredArgsConstructor
public class AutoLogAspect {

    private final LogRepository logRepository;

    /**
     * 系统日志切点
     */
    @Pointcut("@annotation(autoLog)")
    public void autoLogPointCut(AutoLog autoLog) {
    }

    @Around("autoLogPointCut(autoLog)")
    public Object doAround(ProceedingJoinPoint pjp, AutoLog autoLog) throws Throwable {
        long beginTime = System.currentTimeMillis();
        //执行方法
        Object result = pjp.proceed();
        //执行时长(毫秒)
        long time = System.currentTimeMillis() - beginTime;
        //保存日志
        saveSysLog(pjp, time, autoLog,null);
        return result;
    }

    private void saveSysLog(JoinPoint joinPoint, long time, AutoLog autoLog, Throwable throwable) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Log sysLogDO = new Log();
        if (autoLog != null) {
            //注解上的描述,操作日志内容
            sysLogDO.setLogContent(autoLog.value());
            sysLogDO.setLogType(autoLog.logType());
        }
        if (Objects.nonNull(throwable)) {
            sysLogDO.setRequestFinalStatus(1);
        }
        //请求的方法名
        String className = joinPoint.getTarget().getClass().getName();
        String methodName = signature.getName();
        sysLogDO.setMethod(className + "." + methodName + "()");
        //请求的参数
        Object[] args = joinPoint.getArgs();
        List<Object> toJsonArgs = new ArrayList<>();
        try {
            for (Object arg : args) {
                if (!(arg instanceof HttpServletRequest) && !(arg instanceof HttpServletResponse)) {
                    toJsonArgs.add(arg);
                }
            }
            String params = JacksonUtils.X.format(toJsonArgs);
            sysLogDO.setRequestParam(params);
        } catch (Exception e) {
            //noting
            log.error("记录日志失败 ", e);
        }
        //获取request
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        //设置请求地址
        sysLogDO.setRequestUrl(request.getRequestURL().toString());
        //设置IP地址
        sysLogDO.setIp(IpUtils.X.getIpAddr(request));
        sysLogDO.setRequestType(request.getMethod());
        //耗时
        sysLogDO.setCostTime(time);
        sysLogDO.setCreator(SecurityUtils.X.getUser().getUsername());
        sysLogDO.setCreateTime(new Date());
        //保存系统日志
        logRepository.saveLog(sysLogDO);
    }

    @AfterThrowing(pointcut = "autoLogPointCut(autoLog)", throwing = "throwable")
    public void afterThrowing(JoinPoint joinPoint, AutoLog autoLog, Throwable throwable) {
        saveSysLog(joinPoint, 0L, autoLog, throwable);
    }
}
